package com.wormhole.wormholemanagement.service.impl;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.excel.EasyExcel;
import com.wormhole.wormholemanagement.dto.NewsPhotoDTOUpload;
import com.wormhole.wormholemanagement.entity.NewsPerson;
import com.wormhole.wormholemanagement.entity.NewsPhoto;
import com.wormhole.wormholemanagement.entity.OperationLog;
import com.wormhole.wormholemanagement.entity.UnitInfo;
import com.wormhole.wormholemanagement.repository.NewsPhotoRepository;
import com.wormhole.wormholemanagement.repository.OperationLogRepository;
import com.wormhole.wormholemanagement.repository.UnitInfoRepository;
import com.wormhole.wormholemanagement.service.NewsPhotoService;
import com.wormhole.wormholemanagement.utils.MultiPersonInfoExtractor;
import com.wormhole.wormholemanagement.utils.PicExifInfoUtil;
import com.wormhole.wormholemanagement.utils.SnowFlakeUtil;
import com.wormhole.wormholemanagement.utils.SpecificationBuilder;
import jakarta.servlet.http.HttpServletResponse;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

import static com.wormhole.wormholemanagement.utils.ExifWriterUtil.*;

@Slf4j
@Service
public class NewsPhotoServiceImpl implements NewsPhotoService {

    // -------------------------- 移动文件类型枚举类 --------------------------
    public static final Integer NO_MOVE_TYPE=0;//不移管类型
    public static final Integer INFO_MOVE_TYPE=1;//资料移管类型

    @Value("${baseDir}")
    private String baseDir;

    @Value("${noMoveBaseDir}")
    private String noMoveBaseDir;
    @Value("${infoMoveBaseDir}")
    private String infoMoveBaseDir;
    /**
     * 下载类型枚举类
     */
    @Getter
    @AllArgsConstructor
    public enum DownloadType {
        CURR_TABLE("0", "当前表格"),
        ALL("1", "全部数据"),
        MOVE("2","移管"),
        NO_MOVE("3","不移管"),
        CURR_QUERY("4", "当前查询");
        private final String value;
        private final String content;
    }

    // -------------------------- 媒体文件类型配置 --------------------------
    // 音频文件扩展名集合
    private static final Set<String> AUDIO_EXTENSIONS = Set.of("mp3", "wav", "flac", "aac", "ogg", "wma", "m4a");
    // 视频文件扩展名集合
    private static final Set<String> VIDEO_EXTENSIONS = Set.of("mp4", "avi", "mov", "mkv", "flv", "wmv", "mpeg", "mpg");
    // 图片文件扩展名集合
    private static final Set<String> IMAGE_EXTENSIONS = Set.of("jpg", "jpeg", "png", "gif", "bmp", "tiff", "webp", "heic", "raw");

    private final NewsPhotoRepository photoRepository;
    private final OperationLogRepository logRepository;
    private final UnitInfoRepository unitInfoRepository;



    @Autowired
    public NewsPhotoServiceImpl(NewsPhotoRepository photoRepository,
                                OperationLogRepository logRepository, UnitInfoRepository unitInfoRepository) {
        this.photoRepository = photoRepository;
        this.logRepository = logRepository;
        this.unitInfoRepository = unitInfoRepository;
    }

    @Override
    @Transactional
    public NewsPhoto createPhoto(NewsPhoto photo, Long operatorId) {
        photo.setCreatedAt(LocalDateTime.now().toString());
        photo.setUpdatedAt(LocalDateTime.now().toString());
        photo.setOperatorId(operatorId);

        // 处理关联的persons
        if (photo.getPersons() != null) {
            photo.getPersons().forEach(person -> {
                person.setPhoto(photo);  // 建立对象关联
            });
        }

        // 缓存格式转换结果，避免重复调用 toLowerCase
        String imageFormat = photo.getImageFormat().toLowerCase();
        File file = null;
        if(IMAGE_EXTENSIONS.contains(imageFormat)){
            boolean isJpeg = "jpg".equals(imageFormat) || "jpeg".equals(imageFormat);
            boolean isGif = "gif".equals(imageFormat);

            try {
                // 文件路径校验（示例：限制在特定目录内）
                String imagePath = photo.getImagePath();
                if (!isJpeg&&!isGif) {
                    File pngFile = new File(imagePath);
                    file = convertToJpegAndReplace(pngFile);
                    // 更新格式和路径
                    String newPath = imagePath.replaceAll("\\." + imageFormat + "$", ".jpg");
                    photo.setImagePath(newPath);
                    photo.setImageFormat("JPG");
                } else {
                    file = new File(imagePath);
                }
                // 统一日期格式处理
                String dateTimeString =null;
                if(StrUtil.isAllEmpty(photo.getShootingPeriod())){
                    String shootingPeriod="00:00";
                    dateTimeString=photo.getShootingDate() + " "+shootingPeriod;
                }else{
                    dateTimeString = photo.getShootingDate() + " " + photo.getShootingPeriod();
                }
                UnitInfo unitInfo=unitInfoRepository.findByUnitId(Long.valueOf(photo.getOrganization()));
                String unitName = "";
                if(unitInfo!=null){
                    unitName=unitInfo.getUnit_name();
                }
                Date date = DateUtil.parse(dateTimeString, "yyyy-MM-dd HH:mm");
                SimpleDateFormat formatter = new SimpleDateFormat("yyyy:MM:dd HH:mm:ss");
                if(!isGif){
                    try {
                        updateJpegMetadata(file, photo.getImageTitle(),unitName, photo.getJournalists(), formatter.format(date));
                    } catch (Exception e) {
                        System.out.println("更新EXIF元数据失败，尝试重建文件: " + e.getMessage());
                        try {
                            // 如果更新失败，尝试重建文件
                            File standardJpeg = convertToStandardJpeg(file);
                            rebuildJpegWithMetadata(standardJpeg, photo.getImageTitle(),unitName, photo.getJournalists(), formatter.format(date));
                        } catch (Exception rebuildException) {
                            System.out.println("重建文件也失败了: " + rebuildException.getMessage());
                            // 记录日志但不中断流程
                            log.warn("无法更新图片元数据: " + file.getAbsolutePath(), rebuildException);
                        }
                    }
                }
                // 更新格式和路径


            } catch (Exception e) {
                log.error("Failed to update photo metadata", e);
                // 记录详细日志
                throw new RuntimeException("Failed to update photo metadata", e);
            }
        }else{
            String imagePath = photo.getImagePath();
            file = new File(imagePath);
        }
        // 更新图片标题
        if(StrUtil.isAllNotEmpty(photo.getFileNumber())){
            String newFileName = photo.getFileNumber() + "."+ photo.getImageFormat().toLowerCase();
            String newPathWithNewName = photo.getImagePath().substring(0, photo.getImagePath().lastIndexOf(File.separator) + 1) + newFileName;
            file.renameTo(new File(newPathWithNewName));
            photo.setImagePath(newPathWithNewName);
            photo.setImageName(newFileName);
        }

        // 自动级联保存（不需要单独处理ID）
        NewsPhoto savedPhoto = photoRepository.save(photo);

        // Log the operation
        OperationLog log = new OperationLog();
        log.setOperatorId(operatorId);
        log.setOperationType("create");
        log.setTableName("news_photos");
        log.setRecordId(savedPhoto.getPhotoId());
        log.setOperationDetails("Created news photo with ID: " + savedPhoto.getPhotoId());
        log.setOperationTime(LocalDateTime.now());
        logRepository.save(log);

        return savedPhoto;
    }


    @Transactional
    public void createPhotos(List<NewsPhoto> photos) {
        photoRepository.saveAll(photos);
    }

    @Override
    @Transactional
    public NewsPhoto updatePhoto(Long id, NewsPhoto photo, Long operatorId) {
        NewsPhoto existingPhoto = photoRepository.findById(id)
                .orElseThrow(() -> new RuntimeException("Photo not found with id: " + id));

        // 缓存格式转换结果，避免重复调用 toLowerCase
        String imageFormat = photo.getImageFormat().toLowerCase();
        boolean isJpeg = "jpg".equals(imageFormat) || "jpeg".equals(imageFormat);
        // 更新字段
        updatePhotoFields(existingPhoto, photo, operatorId);

        try {
            // 文件路径校验（示例：限制在特定目录内）
            String imagePath = photo.getImagePath();

            File file;
            if (!isJpeg) {
                File pngFile = new File(imagePath);
                file = convertToJpegAndReplace(pngFile);
            } else {
                file = new File(imagePath);
            }
            UnitInfo unitInfo=unitInfoRepository.findByUnitId(Long.valueOf(photo.getOrganization()));
            String unitName = "";
            if(unitInfo!=null){
                unitName=unitInfo.getUnit_name();
            }
            // 统一日期格式处理
            String dateTimeString = photo.getShootingDate() + " " + photo.getShootingPeriod();
            Date date = DateUtil.parse(dateTimeString, "yyyy-MM-dd HH:mm:ss");
            SimpleDateFormat formatter = new SimpleDateFormat("yyyy:MM:dd HH:mm:ss");

            updateJpegMetadata(file, photo.getImageTitle(), unitName,photo.getJournalists(), formatter.format(date));

            // 更新格式和路径
            String newPath = imagePath.replaceAll("\\." + imageFormat + "$", ".jpg");
            existingPhoto.setImagePath(newPath);
            existingPhoto.setImageFormat("JPG");

        } catch (Exception e) {
            // 记录详细日志
            throw new RuntimeException("Failed to update photo metadata", e);
        }
        NewsPhoto updatedPhoto = photoRepository.save(existingPhoto);
        // 记录操作日志
        logOperation(operatorId, updatedPhoto.getPhotoId());

        return updatedPhoto;
    }

    // 抽离字段更新逻辑
    private void updatePhotoFields(NewsPhoto existing, NewsPhoto updated, Long operatorId) {
        existing.setFileNumber(updated.getFileNumber());
        existing.setImagePath(updated.getImagePath());
        existing.setImageName(updated.getImageName());
        existing.setCompositeTitle(updated.getCompositeTitle());
        existing.setImageTitle(updated.getImageTitle());
        existing.setShootingDate(updated.getShootingDate());
        existing.setShootingPeriod(updated.getShootingPeriod());
        existing.setShootingLocation(updated.getShootingLocation());
        existing.setEventReason(updated.getEventReason());
        existing.setOrganization(updated.getOrganization());
        existing.setMovedToArchive(updated.getMovedToArchive());
        existing.setExtendedData(updated.getExtendedData());
        existing.setUpdatedAt(LocalDateTime.now().toString());
        existing.setOperatorId(operatorId);
    }

    // 抽离日志记录逻辑
    private void logOperation(Long operatorId, Long photoId) {
        OperationLog log = new OperationLog();
        log.setOperatorId(operatorId);
        log.setOperationType("update");
        log.setTableName("news_photos");
        log.setRecordId(photoId);
        log.setOperationDetails("Updated news photo with ID: " + photoId);
        log.setOperationTime(LocalDateTime.now());
        logRepository.save(log);
    }

    @Override
    @Transactional
    public void deletePhoto(Long id, Long operatorId) {
        NewsPhoto photo = photoRepository.findById(id)
                .orElseThrow(() -> new RuntimeException("Photo not found with id: " + id));

        photoRepository.delete(photo);

        // Log the operation
        OperationLog log = new OperationLog();
        log.setOperatorId(operatorId);
        log.setOperationType("delete");
        log.setTableName("news_photos");
        log.setRecordId(id);
        log.setOperationDetails("Deleted news photo with ID: " + id);
        log.setOperationTime(LocalDateTime.now());
        logRepository.save(log);
    }

    @Override
    public NewsPhoto getPhotoById(Long id) {
        return photoRepository.findById(id)
                .orElseThrow(() -> new RuntimeException("Photo not found with id: " + id));
    }

    /**
     * 根据文件编号获取图片信息
     *
     * @param fileNumber 文件编号
     * @return 图片信息
     */
    public NewsPhoto getPhotoByFileNumber(String fileNumber) {
        return photoRepository.findByFileNumber(fileNumber);
    }
    @Override
    public Page<NewsPhoto> getAllPhotos(Pageable pageable) {
        return photoRepository.findAll(pageable);
    }

    @Override
    public Page<NewsPhoto> searchPhotos(Specification<NewsPhoto> spec, Pageable pageable) {
        return photoRepository.findAll(spec, pageable);
    }

    public void importPhoto(String path) {
        List<NewsPhoto> newsPhotos = new ArrayList<>();
        path = baseDir + File.separator + path;

        // 检查路径是否有效
        File directory = new File(path);
        if (!directory.exists() || !directory.isDirectory()) {
            throw new IllegalArgumentException("Invalid directory path: " + path);
        }

        // 递归遍历目录
        traverseDirectory(directory, newsPhotos);

        if (newsPhotos.isEmpty()) {
            System.out.println("No image files found in the directory and its subdirectories.");
            return;
        }

        try {
            this.createPhotos(newsPhotos);
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }
    /**
     * 递归遍历目录及其子目录
     */
    private void traverseDirectory(File directory, List<NewsPhoto> newsPhotos) {
        File[] files = directory.listFiles();
        if (files == null) {
            return;
        }

        for (File file : files) {
            if (file.isDirectory()) {
                // 递归处理子目录
                traverseDirectory(file, newsPhotos);
            } else if (file.isFile()) {
                // 处理文件
                processFile(file, newsPhotos);
            }
        }
    }
    /**
     * 处理单个文件
     */
    private void processFile(File file, List<NewsPhoto> newsPhotos) {
        String fileName = file.getName().toLowerCase();
        // 定义支持的图片格式
        List<String> supportedFormats = Arrays.asList(".jpg", ".jpeg", ".tiff", ".tif", ".png", ".bmp", ".gif");
        if (supportedFormats.stream().noneMatch(fileName::endsWith)) {
            NewsPhoto photo = new NewsPhoto();
            // 获取文件信息
            String imagePath = file.getPath();
            String imageName = file.getName();
            int lastDotIndex = fileName.lastIndexOf('.');
            String imageFormat = fileName.substring(lastDotIndex + 1).toUpperCase();
            long imageSize = file.length() / 1024; // 图片大小（KB）
            // 设置文件属性
            photo.setImagePath(imagePath);
            photo.setImageName(imageName);
            photo.setImageFormat(imageFormat);
            photo.setImageSize(imageSize + " KB");
            photo.setImageScale("");
            photo.setImageDpi("");
            photo.setCreatedAt(LocalDateTime.now().toString());
            photo.setUpdatedAt(LocalDateTime.now().toString());
            photo.setOperatorId(SnowFlakeUtil.getId().longValue());
            photo.setFileSize(imageSize);
            photo.setFileType(getFileType(imageName));
            newsPhotos.add(photo);
        }else {
            try {
                NewsPhoto photo = new NewsPhoto();
                System.out.println("----------------------------------------\n" + file.getName());
                Map exifMap = PicExifInfoUtil.readPicExifInfo(file);

                // 获取照片信息
                String imagePath = file.getPath();
                String imageName = file.getName();
                int lastDotIndex = fileName.lastIndexOf('.');
                String imageFormat = fileName.substring(lastDotIndex + 1).toUpperCase();
                long imageSize = file.length() / 1024; // 图片大小（KB）
                String imageWidth = getSafeValue(exifMap, "Image Width"); // 图片宽度
                if (StrUtil.isAllNotEmpty(imageWidth)) {
                    imageWidth = imageWidth.replace(" pixels", "");
                }
                String imageHeight = getSafeValue(exifMap, "Image Height"); // 图片高度
                if (StrUtil.isAllNotEmpty(imageHeight)) {
                    imageHeight = imageHeight.replace(" pixels", "");
                }
                String imageDpi = getSafeValue(exifMap, "X Resolution"); // 图片 DPI
                if (StrUtil.isAllNotEmpty(imageDpi)) {
                    imageDpi = imageDpi.replace(" dots per inch", "");
                }

                // 获取拍摄时间
                String shootingDateTimeOriginal = getSafeValue(exifMap, "Date/Time Original");
                String shootingDateTimeDigitized = getSafeValue(exifMap, "Date/Time Digitized");
                String shootingDateTime = getSafeValue(exifMap, "Date/Time");

                // 优先使用 shootingDateTimeOriginal，其次是 shootingDateTimeDigitized，最后是 shootingDateTime
                String selectedDateStr = !shootingDateTimeOriginal.isEmpty()
                        ? shootingDateTimeOriginal
                        : (!shootingDateTimeDigitized.isEmpty()
                        ? shootingDateTimeDigitized
                        : shootingDateTime);

                // 解析日期
                if (!selectedDateStr.isEmpty()) {
                    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy:MM:dd HH:mm:ss");
                    try {
                        LocalDateTime selectedDate = LocalDateTime.parse(selectedDateStr, formatter);
                        photo.setShootingDate(selectedDate.format(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
                        photo.setShootingPeriod(selectedDate.format(DateTimeFormatter.ofPattern("HH:mm:ss")));
                    } catch (Exception e) {
                        System.err.println("Failed to parse date: " + selectedDateStr);
                    }
                }
                // 设置照片属性
                photo.setImagePath(imagePath);
                photo.setImageName(imageName);
                photo.setImageFormat(imageFormat);
                photo.setImageSize(imageSize + " KB");
                photo.setImageScale(imageWidth + "x" + imageHeight);
                photo.setImageDpi(imageDpi);
                photo.setCreatedAt(LocalDateTime.now().toString());
                photo.setUpdatedAt(LocalDateTime.now().toString());
                photo.setOperatorId(SnowFlakeUtil.getId().longValue());
                photo.setFileSize(imageSize);
                photo.setFileType(getFileType(imageName));
                newsPhotos.add(photo);
            } catch (Exception e) {
                System.err.println("Failed to process file: " + file.getAbsolutePath());
                e.printStackTrace();
            }
        }
    }

    @Transactional
    public List<NewsPhoto> batchUpdate(List<NewsPhoto> newsPhotos) {
        return photoRepository.saveAll(newsPhotos);
    }

    // 自定义批量更新
    @Transactional
    public int batchUpdateArchiveStatus(List<Long> photoIds, Boolean isMovedToArchive) {
        log.info("批量更新归档状态，IDs: {}, 状态: {}", photoIds, isMovedToArchive);
        return photoRepository.batchUpdateArchiveStatus(photoIds, isMovedToArchive);
    }

    @Transactional
    public void batchMoveArchive(List<Long> photoIds,Integer moveType) {
        log.info("批量移动归档文件，IDs: {}", photoIds);
        List<NewsPhoto> existingPhotos = photoRepository.findAllById(photoIds);
        for (NewsPhoto photo : existingPhotos) {
            try {
                if(moveType==INFO_MOVE_TYPE&&ObjectUtil.isAllEmpty(photo.getMovedToArchive())){
                    //迁移到资料文件夹
                    moveFileToNoMoveDirectory(photo,infoMoveBaseDir);
                }else if(moveType==NO_MOVE_TYPE&&!photo.getMovedToArchive()){
                    // 迁移文件到不移管目录
                    moveFileToNoMoveDirectory(photo,noMoveBaseDir);
                }
                // 删除数据库记录
                photoRepository.deleteById(photo.getPhotoId());
            } catch (Exception e) {
                // 记录错误但继续处理其他文件
                log.error("处理照片失败，ID: {}, 错误: {}", photo.getPhotoId(), e.getMessage());
            }
        }
    }

    /**
     * 将文件移动到不移管目录
     */
    /**
     * 将文件移动到对应目录，处理路径符号保持一致性
     */
    private void moveFileToNoMoveDirectory(NewsPhoto photo,String moveBaseDir) throws IOException {
        String oldPath = photo.getImagePath();

        // 标准化路径分隔符（统一使用正斜杠）
        oldPath = oldPath.replace("\\", "/");

        String normalizedBaseDir = baseDir.replace("\\", "/");
        String normalizedNoMoveBaseDir = moveBaseDir.replace("\\", "/");

        // 确保目录路径以斜杠结尾
        if (!normalizedBaseDir.endsWith("/")) {
            normalizedBaseDir += "/";
        }
        if (!normalizedNoMoveBaseDir.endsWith("/")) {
            normalizedNoMoveBaseDir += "/";
        }

        // 替换路径前缀
        if (oldPath.startsWith(normalizedBaseDir)) {
            String newPath = oldPath.replaceFirst(
                    Pattern.quote(normalizedBaseDir),
                    Matcher.quoteReplacement(normalizedNoMoveBaseDir)
            );

            // 标准化新路径
            newPath = newPath.replace("\\", "/");

            // 创建目标目录
            Path newDirectory = Paths.get(newPath).getParent();
            if (newDirectory != null) {
                Files.createDirectories(newDirectory);
            }

            // 移动文件
            Path sourcePath = Paths.get(oldPath);
            Path targetPath = Paths.get(newPath);

            // 如果源文件不存在，记录日志并返回
            if (!Files.exists(sourcePath)) {
                log.warn("源文件不存在: {}", oldPath);
                return;
            }
            Files.move(sourcePath, targetPath, StandardCopyOption.REPLACE_EXISTING);
            // 更新数据库中的路径
//            photo.setImagePath(newPath);
        } else {
            log.warn("文件路径不以基础目录开头，跳过移动: {}", oldPath);
        }
    }

    // 获取安全的值
    private static String getSafeValue(Map map, String key) {
        Object value = map.get(key);
        return value != null ? value.toString() : "";
    }

    /**
     * 通过路径重命名文件夹
     *
     * @param oldFolderPath      原始文件夹路径
     * @param newFolderName 新文件夹名称
     * @return 是否成功重命名
     */
    @Transactional
    public boolean renameFolder(String oldFolderPath, String newFolderName) {
        // 验证输入参数
        if (oldFolderPath == null || oldFolderPath.isEmpty() || newFolderName == null || newFolderName.isEmpty()) {
            throw new IllegalArgumentException("路径或新文件夹名称不能为空");
        }

        Path oldPath = Paths.get(oldFolderPath);

        // 验证原文件夹是否存在
        if (!Files.isDirectory(oldPath)) {
            throw new IllegalArgumentException("原路径不存在或不是文件夹: " + oldFolderPath);
        }

        // 构建新文件夹路径
        Path parentPath = oldPath.getParent();
        if (parentPath == null) {
            throw new IllegalArgumentException("无法获取父目录: " + oldFolderPath);
        }
        Path newPath = parentPath.resolve(newFolderName);

        // 检查新文件夹是否已存在
        if (Files.exists(newPath)) {
            throw new RuntimeException("新文件夹已存在: " + newPath.toString());
        }

        // 使用NIO的move方法重命名文件夹
        try {
            Files.move(oldPath, newPath,
                    StandardCopyOption.ATOMIC_MOVE,  // 确保操作的原子性
                    StandardCopyOption.REPLACE_EXISTING);  // 如果存在则替换（谨慎使用）
        } catch (IOException e) {
            throw new RuntimeException(e);
        }


        // 更新数据库中的路径
        try {
            updateDatabasePaths(oldPath.toString(), newPath.toString());
            System.out.println("文件夹重命名成功: " + oldPath + " -> " + newPath);
        } catch (Exception e) {
            System.out.println("更新数据库路径失败: " + e.getMessage());
            throw new RuntimeException("更新数据库路径失败", e);
        }

        return true;
    }
    /**
     * 更新数据库中包含旧路径的记录
     *
     * @param oldPath 原始文件夹路径
     * @param newPath 新文件夹路径
     */
    private void updateDatabasePaths(String oldPath, String newPath) {
        // 查询所有 imagePath 包含 oldPath 的记录
        List<NewsPhoto> photos = photoRepository.findByImagePathContaining(oldPath);

        if (photos.isEmpty()) {
            System.out.println("未找到需要更新的数据库记录");
            return;
        }

        // 替换路径并更新记录
        for (NewsPhoto photo : photos) {
            String updatedPath = photo.getImagePath().replace(oldPath, newPath);
            photo.setImagePath(updatedPath);
            photo.setUpdatedAt(LocalDateTime.now().toString()); // 更新时间戳

        }

        // 批量保存更新后的记录
        photoRepository.saveAll(photos);
        System.out.println("已更新 " + photos.size() + " 条数据库记录");
    }

    /**
     * 辅助方法：格式化文件大小（字节 → KB/MB）
     */
    private static String formatFileSize(long bytes) {
        if (bytes < 1024) {
            return bytes + " B";
        } else if (bytes < 1024 * 1024) {
            return String.format("%.2f KB", bytes / 1024.0);
        } else {
            return String.format("%.2f MB", bytes / (1024.0 * 1024.0));
        }
    }

    public void downloadZip(HttpServletResponse response, String downloadType, Map<String, String> params,Pageable pageable) {
        // 1. 解析参数：将逗号分隔的路径字符串转为List<String>
        List<String> movePaths = new ArrayList<>();
        List<String> noMovePaths = new ArrayList<>();
        List<NewsPhoto> photoList = null;

        if(downloadType.equals(DownloadType.ALL.getValue()) || downloadType.equals(DownloadType.MOVE.getValue()) || downloadType.equals(DownloadType.NO_MOVE.getValue())){
            photoList= photoRepository.findAll();
        }else if (downloadType.equals(DownloadType.CURR_TABLE.getValue())){
            Specification<NewsPhoto> spec = SpecificationBuilder.buildSpecification(params);
            Page<NewsPhoto> records = photoRepository.findAll(spec, pageable);
            photoList=records.getContent();
        }else if(downloadType.equals(DownloadType.CURR_QUERY.getValue())){
            Specification<NewsPhoto> spec = SpecificationBuilder.buildSpecification(params);
            photoList= photoRepository.findAll(spec);
        }
        if(photoList != null) {
            for(NewsPhoto photo:photoList){
                if(photo.getIsMovedToArchive()!=null && photo.getIsMovedToArchive()){
                    movePaths.add(photo.getImagePath());
                }else{
                    noMovePaths.add(photo.getImagePath());
                }
            }
        }

        List<UnitInfo> unitInfoList = unitInfoRepository.findAll();
        String unitName = "default"; // 提供默认值
        if(ObjectUtil.isNotEmpty(unitInfoList) && unitInfoList.get(0) != null){
            unitName = unitInfoList.get(0).getUnit_name();
            if(StrUtil.isBlank(unitName)) {
                unitName = "default";
            }
        }

        // 2. 修复：中文文件名URL编码（关键步骤）
        String zipFileName = unitName + ".zip";
        // 对中文文件名进行URL编码，避免响应头非法
        String encodedFileName = null;
        try {
            encodedFileName = URLEncoder.encode(zipFileName, StandardCharsets.UTF_8.name());
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException(e);
        }

        // 配置响应头：使用编码后的文件名，兼容所有浏览器
        response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
        response.setHeader(HttpHeaders.CONTENT_DISPOSITION,
                "attachment; filename=\"" + encodedFileName + "\"; filename*=UTF-8''" + encodedFileName);
        // 明确响应编码
        response.setCharacterEncoding(StandardCharsets.UTF_8.name());

        // 3. 直接通过响应输出流生成ZIP（不落地到本地磁盘）
        try (ZipOutputStream zos = new ZipOutputStream(response.getOutputStream())) {
            switch (downloadType) {
                case "0": // CURR_TABLE
                case "1": // ALL
                    // 处理移馆文件
                    processFilePaths(movePaths, unitName, "移馆", zos);
                    // 处理不移馆文件
                    processFilePaths(noMovePaths, unitName, "不移馆", zos);
                    break;
                case "2": // MOVE
                    // 处理移馆文件
                    processFilePaths(movePaths, unitName, "移馆", zos);
                    break;
                case "3": // NO_MOVE
                    // 处理不移馆文件
                    processFilePaths(noMovePaths, unitName, "不移馆", zos);
                    break;
                default:
                    // 处理移馆文件
                    processFilePaths(movePaths, unitName, "移馆", zos);
                    // 处理不移馆文件
                    processFilePaths(noMovePaths, unitName, "不移馆", zos);
                    break;
            }
            // 刷新流，确保所有数据写入响应
            zos.flush();
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException("ZIP文件生成失败: " + e.getMessage(), e);
        }
    }


    // -------------------------- 核心工具方法 --------------------------
    /**
     * 解析路径参数：将"路径1,路径2"格式转为List<String>
     */
    private List<String> parsePathParam(String pathStr) {
        if (pathStr == null || pathStr.trim().isEmpty()) {
            return new ArrayList<>();
        }
        return Arrays.asList(pathStr.split(","));
    }

    /**
     * 处理文件路径列表，生成ZIP条目并写入流
     */
    private void processFilePaths(List<String> filePaths,
                                  String unitName,
                                  String moveStatus,
                                  ZipOutputStream zos) throws IOException {
        if (filePaths.isEmpty()) {
            System.out.println("没有" + moveStatus + "文件需要处理");
            return;
        }

        // 标准化基础目录（处理Windows反斜杠和Linux正斜杠）
        baseDir = baseDir.replace("/", File.separator);
        if (!baseDir.endsWith(File.separator)) {
            baseDir += File.separator;
        }

        for (String path : filePaths) {
            File file = new File(path.trim());
            // 跳过无效文件（不存在或不是文件）
            if (!file.exists() || !file.isFile()) {
                System.err.println("无效文件，跳过：" + path);
                continue;
            }

            // 1. 判断文件类型（音频/视频/照片）
            String fileTypeDir = getFileTypeDir(file.getName());
            if (fileTypeDir == null) {
                System.out.println("不支持的文件类型，跳过：" + file.getName());
                continue;
            }

            // 2. 提取基础目录后的相对路径（保留2025/9月/0901/军训结构）
            String absolutePath = file.getAbsolutePath();
//            if (!absolutePath.startsWith(baseDir)) {
//                System.err.println("文件不在基础目录下，跳过：" + absolutePath);
//                continue;
//            }
            String relativePath = absolutePath.substring(baseDir.length()); // 结果如"2025\\9月\\0901\\军训\\photo.jpg"

            // 3. 验证路径包含基准年份（如"2025"）
//            if (!relativePath.startsWith(baseYear + File.separator)) {
//                System.err.println("文件不在" + baseYear + "目录下，跳过：" + absolutePath);
//                continue;
//            }

            // 4. 构建ZIP内部目录结构（单位名称/移馆/音频/2025/9月/0901/军训/photo.jpg）
            String zipEntryName = String.join("/",
                    unitName,
                    moveStatus,
                    fileTypeDir,
                    relativePath.replace(File.separator, "/") // 统一为ZIP标准正斜杠
            );

            // 5. 写入ZIP条目
            zos.putNextEntry(new ZipEntry(zipEntryName));
            // 读取源文件并写入ZIP流
            try (InputStream is = new FileInputStream(file)) {
                byte[] buffer = new byte[4096]; // 4KB缓冲区，提升效率
                int len;
                while ((len = is.read(buffer)) != -1) {
                    zos.write(buffer, 0, len);
                }
            }
            zos.closeEntry(); // 关闭当前条目，准备下一个
            System.out.println("已添加到ZIP：" + zipEntryName);
        }
    }

    /**
     * 根据文件名判断文件类型目录（音频/视频/照片）
     */
    private String getFileTypeDir(String fileName) {
        int dotIndex = fileName.lastIndexOf('.');
        if (dotIndex == -1 || dotIndex == fileName.length() - 1) {
            return null; // 无扩展名或只有点（如".tmp"）
        }
        String extension = fileName.substring(dotIndex + 1).toLowerCase();
        if (AUDIO_EXTENSIONS.contains(extension)) {
            return "音频";
        } else if (VIDEO_EXTENSIONS.contains(extension)) {
            return "视频";
        } else if (IMAGE_EXTENSIONS.contains(extension)) {
            return "照片";
        } else {
            return null;
        }
    }

    /**
     * 根据文件名判断档案门类
     */
    private String getFileType(String fileName) {
        int dotIndex = fileName.lastIndexOf('.');
        if (dotIndex == -1 || dotIndex == fileName.length() - 1) {
            return null; // 无扩展名或只有点（如".tmp"）
        }
        String extension = fileName.substring(dotIndex + 1).toLowerCase();
        if (AUDIO_EXTENSIONS.contains(extension)) {
            return "LY";
        } else if (VIDEO_EXTENSIONS.contains(extension)) {
            return "LX";
        } else if (IMAGE_EXTENSIONS.contains(extension)) {
            return "ZP";
        } else {
            return null;
        }
    }

    /**
     * 导入Excel文件
     */
    public void importExcel(MultipartFile file){
        try {
            List<NewsPhotoDTOUpload> newsPhotos = EasyExcel.read(file.getInputStream())
                    .head(NewsPhotoDTOUpload.class)
                    .sheet()
                    .doReadSync();
            for(NewsPhotoDTOUpload newsPhoto:newsPhotos){
                NewsPhoto photo=this.photoRepository.findByImageName(newsPhoto.getFileNumber());
                if(photo!=null){
                    photo.setFileNumber(newsPhoto.getFileNumber());
                    photo.setImageTitle(newsPhoto.getImageTitle());
                    photo.setShootingDate(newsPhoto.getShootingDate());
                    photo.setShootingLocation(newsPhoto.getShootingLocation());
                    photo.setJournalists(newsPhoto.getJournalists());
                    photo.setImageBackground(newsPhoto.getImageBackground());

                    List<NewsPerson> persons=new ArrayList<>();
                    // 提取所有人物信息
                    List<Map<String, String>> personMaps = MultiPersonInfoExtractor.extractPersonsToMap(newsPhoto.getPersons());
                    // 打印结果
                    for (Map<String, String> personMap : personMaps) {
                        NewsPerson person=new NewsPerson();
                        person.setName(personMap.get("name"));
                        person.setPosition(personMap.get("position"));
                        person.setPositionInImage(personMap.get("location"));
                    }
                    photo.setPersons(persons);
                    this.photoRepository.save(photo);
                }
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
    /**
     * 获取所有图片路径
     */
    @Override
    public List<String> getAllImagePaths(){
        return this.photoRepository.findAllImagePaths();
    }

    /**
     * 生成文件编号
     */
    @Override
    @Transactional
    public void genFileNumber(String path) {
        path=File.separator+path;
        // 根据路径查找照片
        List<NewsPhoto> newsPhotos = this.photoRepository.findByPath(path);
        int sheetNum = 1; // 从1开始编号
        Set<String> occupiedPaths = new HashSet<>(); // 已占用的目标路径
        for (NewsPhoto newsPhoto : newsPhotos) {
            String fileNumber = newsPhoto.getFileNumber();
            String imagePath = newsPhoto.getImagePath();

            // 提取文件名（不含扩展名）
            String fileNameWithoutExtension = newsPhoto.getImageName();
            if (fileNameWithoutExtension.contains(".")) {
                fileNameWithoutExtension = fileNameWithoutExtension.substring(0, fileNameWithoutExtension.lastIndexOf("."));
            }

            // 获取文件扩展名
            String fileExtension = ".jpg";
            if (newsPhoto.getImageName().contains(".")) {
                fileExtension = newsPhoto.getImageName().substring(newsPhoto.getImageName().lastIndexOf("."));
            }

            // 替换文件编号中的最后4位数字
            if (fileNumber.length() >= 4) {
                // 找到最后的4位数字并替换
                String newFileNumber = fileNumber.replaceFirst("(\\d+)$", String.format("%04d", sheetNum));
                // 更新文件名
                String newImageName = newFileNumber + fileExtension;

                // 更新路径中的文件名
                String newPath = imagePath;
                if (imagePath.contains(fileNameWithoutExtension + fileExtension)) {
                    newPath = imagePath.replace(fileNameWithoutExtension + fileExtension, newImageName);
                } else {
                    // 如果原文件名不匹配，直接替换最后的文件名部分
                    int lastSeparator = Math.max(imagePath.lastIndexOf("/"), imagePath.lastIndexOf("\\"));
                    if (lastSeparator >= 0) {
                        newPath = imagePath.substring(0, lastSeparator + 1) + newImageName;
                    }
                }

                // 重命名磁盘上的文件
                try {
                    File oldFile = new File(imagePath);
                    File newFile = new File(newPath);

                    // 检查原文件是否存在
                    if (oldFile.exists()) {
                        // 如果新文件名的目录不存在，创建目录
                        File parentDir = newFile.getParentFile();
                        if (parentDir != null && !parentDir.exists()) {
                            parentDir.mkdirs();
                        }
                        // 重命名文件
                        boolean renamed = oldFile.renameTo(newFile);
                        if (!renamed) {
                            log.info("无法重命名文件: {} -> {}", imagePath, newPath);
                            // 如果重命名失败，尝试使用Files.move方法
                            String tempPath = newPath + ".tmp";
                            try {
                                Files.move(oldFile.toPath(), new File(tempPath).toPath());
                                occupiedPaths.add(tempPath);
                            } catch (Exception e) {
                                log.info("无法重命名文件: {} -> {}", imagePath, tempPath);
                                throw new RuntimeException("使用Files.move方法重命名文件时出错", e);
                            }
                        }
                        // 更新实体
                        newsPhoto.setFileNumber(newFileNumber);
                        newsPhoto.setImageName(newImageName);
                        newsPhoto.setImagePath(newPath);
                    } else {
                        log.warn("原文件不存在: {}", imagePath);
                    }
                } catch (Exception e) {
                    log.error("重命名文件时出错: {}", e.getMessage());
                    throw new RuntimeException("重命名文件时出错", e);
                }
                // 保存更新
                this.photoRepository.save(newsPhoto);
            }
            sheetNum++;
        }
        // 3. 统一还原所有tmp文件（去掉.tmp后缀）
        restoreTmpFiles(occupiedPaths);
    }
    /**
     * 遍历目标目录，将所有.tmp文件还原为原文件名
     */
    private void restoreTmpFiles(Set<String> occupiedPaths) {
        for (String tempPath : occupiedPaths) {
            // 检查是否为.tmp文件
            if (tempPath.endsWith(".tmp")) {
                // 去掉.tmp后缀得到原始文件路径
                String originalPath = tempPath.substring(0, tempPath.length() - 4);

                File tempFile = new File(tempPath);
                File originalFile = new File(originalPath);

                // 检查临时文件是否存在
                if (tempFile.exists()) {
                    try {
                        // 重命名临时文件为原始文件名
                        boolean restored = tempFile.renameTo(originalFile);
                        if (!restored) {
                            // 如果renameTo失败，尝试使用Files.move
                            try {
                                Files.move(tempFile.toPath(), originalFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
                            } catch (IOException e) {
                                log.error("无法将临时文件还原为原始文件: {} -> {}, 错误: {}", tempPath, originalPath, e.getMessage());
                            }
                        } else {
                            log.info("成功将临时文件还原为原始文件: {} -> {}", tempPath, originalPath);
                        }
                    } catch (Exception e) {
                        log.error("还原临时文件时出错: {} -> {}, 错误: {}", tempPath, originalPath, e.getMessage());
                    }
                } else {
                    log.warn("临时文件不存在: {}", tempPath);
                }
            }
        }
    }

    public static void main(String[] args) {
        String fileNumber = " 0003-ZP•2022-D30-RMT-20017";
        int sheetNum = 1; // 从1开始编号
        String newFileNumber = fileNumber.replaceFirst("(\\d+)$", String.format("%04d", sheetNum));
        System.out.println(newFileNumber);
    }
}